home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 10 / develop 10 code / Is it Art? / ArtMaker / PaintApp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-08  |  21.7 KB  |  840 lines  |  [TEXT/KAHL]

  1. #include    "Painterly.h"
  2.  
  3. /* The one shell global we need */
  4. extern MenuHandle    gShellMenuHandles[];
  5.  
  6. /* Painterly Globals */
  7. MenuHandle        gPaintMenuHandles[kNumPaintMenus];     /* The menus we add to the shell */
  8. DocumentRecord    gSrcDoc, gDstDoc;                    /* The doc records */
  9. CWindowPtr        gSrcWindPtr, gDstWindPtr;    /* pointers to the windows, for convenience */
  10. CWindowPtr        gUndoTarget = nil;    /* pointer to the window where the last undo operation happened */
  11. GWorldPtr        gUndoBuffer;                /* a buffer to allow undo */
  12. Handle            gCurrentBrushHandle = nil;    /* handle to the current Brush code */
  13. short            gCurrentBrushNum;            /* the current Brush menu item */
  14. BrushParams        gBrushStuff;                /* the parameter structure for brush calls */
  15. Point            gNextPoint = {0, 0};        /* the next point touched in ordered strokes */
  16. short            gOrderedIncrement = 16;        /* the number of pixels to skip in ordered strokes */
  17. Boolean            gPaintingNow = false, gRandomStrokes = true; /* State Booleans */
  18. THPrint            gPrintRecHandle;            /* The print record */
  19. RGBColor        gBGColor = {32767, 32767, 32767}; /* The bg color of the destination window */
  20. short            gDocTitleHeight, gDocFrameWidth; /* Window Stats, for use in positioning
  21.                                                     and growing the windows */
  22.  
  23. /* Called by the Shell at startup time */
  24. Boolean AppInit(void)
  25. {
  26.     /* Init only happens once, so I put this routine in a separate segment */
  27.     return PaintInit();    
  28. }
  29.  
  30. /* Called when the shell receives an Activate event. It just hides the scrollbars and
  31. the grow box on de-activate, or shows them on activate.
  32.  
  33. **Note: I used to do lots of fancy stuff here, like calling ValidRect on the control 
  34. rects to avoid redrawing in the AppUpdate routine, but I just got in trouble. This is 
  35. easier, if not quite as efficient, and cosmetically it's fine (you can't tell if the 
  36. scrollbar gets drawn twice by looking at it). */
  37.  
  38. void AppActivate(WindowPtr wind, Boolean activate)
  39. {
  40.     DocumentPeek    doc;
  41.     
  42.     if(IsAppWindow(wind) == true)
  43.     {
  44.         doc = (DocumentPeek)wind;
  45.         SetPort(wind);
  46.         if(activate)     // an activate event
  47.         {
  48.             
  49.             /* the growbox and controls must be redrawn on activation. */
  50.             DrawGrowIcon(wind);
  51.             ShowControl(doc->hScroll);
  52.             ShowControl(doc->vScroll);
  53.         }
  54.         else                                // a deactivate event
  55.         {
  56.             /* the growbox and controls must be redrawn on de-activation, too. */
  57.             HideControl(doc->hScroll);
  58.             HideControl(doc->vScroll);
  59.             DrawGrowIcon(wind);
  60.         }
  61.     }
  62. }
  63.  
  64. /* Called when a window needs updating. BeginUpdate() has already been called, and the 
  65. port is set to the appropriate window */
  66. void AppUpdate(EventRecord *event)
  67. {
  68.     WindowPtr        wind;
  69.     Rect            updateRect;
  70.     
  71.     wind = (WindowPtr)event->message;
  72.  
  73.     /* Close up the clip to exclude the scroll bars before drawing content */
  74.     CloseClip(wind);
  75.     updateRect = (*wind->clipRgn)->rgnBBox;
  76.     OffToWindow(wind, &updateRect);
  77.     
  78.     /* Reset the clip */
  79.     ClipRect(&wind->portRect);
  80.  
  81.     /* Draw the growbox and controls */    
  82.     DrawGrowIcon(wind);
  83.     if(FrontWindow() == wind)
  84.     {
  85.         UpdtControl(wind, wind->visRgn);
  86.     }
  87.     
  88. }
  89.  
  90. /* Called when the shell recieves a null event. Here's where painting happens if Auto-
  91. Paint is on. */
  92. void AppIdle(EventRecord *Event)
  93. {
  94.     /* Check if we are painting */
  95.     if(gPaintingNow)
  96.     {
  97.         Rect    offRect;
  98.     
  99.         /* Get the rect of the destination */
  100.         offRect = gDstDoc.world->portRect;
  101.  
  102.         if(gRandomStrokes)
  103.         {
  104.             Point    pt;
  105.             
  106.             /* Get a Random point in the offRect */
  107.             pt.h = abs(Random() % (offRect.right - offRect.left));
  108.             pt.v = abs(Random() % (offRect.bottom - offRect.top));
  109.  
  110.             /* Stroke it */
  111.             StrokeBoth(pt);
  112.         }
  113.         else    /* Ordered Strokes */
  114.         {
  115.             /* Stroke the previously saved next point */
  116.             StrokeBoth(gNextPoint);
  117.             
  118.             /* Increment the point for next time */
  119.             gNextPoint.h += gOrderedIncrement;
  120.             
  121.             /* if we fell off the right edge, wrap around. If we're done, stop painting */
  122.             if(gNextPoint.h >= offRect.right)
  123.             {
  124.                 gNextPoint.h = 0;
  125.                 gNextPoint.v += gOrderedIncrement;
  126.                 if(gNextPoint.v >= offRect.bottom) /* Need to stop painting */
  127.                     DoPaintMenu(iStartPainting);
  128.             }
  129.         }
  130.     }
  131. }
  132.  
  133. /* Called when there is a click in the content of a window. The port is already set to 
  134. the window, and thePt is in local coords. Manual painting happens here, as does scroll
  135. bar stuff. */
  136. void AppClick(Point thePt, WindowPtr whichWindptr, Boolean doubleClick)
  137. {
  138.     if(IsAppWindow(whichWindptr) == true)
  139.     {
  140.         Point            lastPoint = {0, 0}, newPt;
  141.         short            part, value;
  142.         ControlHandle    control;
  143.         DocumentPeek    doc;
  144.  
  145.         /* Get the doc */
  146.         doc = (DocumentPeek)whichWindptr;
  147.         
  148.         /* First find out if the click is in a scrollbar: if so deal with it */
  149.         part = FindControl(thePt, whichWindptr, &control);
  150.         if(part != 0)
  151.         {
  152.             switch ( part )
  153.             {
  154.                 /* The thumb is special, and doesn't use the Action Proc */
  155.                 case inThumb: 
  156.                     value = GetCtlValue(control);
  157.                     part = TrackControl(control, thePt, nil);
  158.                     if ( part != 0 ) /* Good control hit */
  159.                     { 
  160.                         value -= GetCtlValue(control);
  161.                         /* value now has CHANGE in value; if value changed, scroll */
  162.                         if ( value != 0 )
  163.                             if ( control == doc->vScroll )
  164.                                 ScrollPict(0, value, whichWindptr);
  165.                             else
  166.                                 ScrollPict(value, 0, whichWindptr);
  167.                     }
  168.                     break;
  169.                     
  170.                 default:    /* they clicked in an arrow, so track & scroll */
  171.                     value = TrackControl(control, thePt, (ProcPtr) ScrollActionProc);
  172.                     break;
  173.             }
  174.         }
  175.         else    /* Not a control click: Paint instead */
  176.         {
  177.             /* Allow an Undo */
  178.             SetUpForUndo(gDstWindPtr);
  179.             
  180.             /* Keep painting while the mouse is down */
  181.             while(StillDown())
  182.             {
  183.                 /* Get the mouse location */
  184.                 SetPort(whichWindptr);
  185.                 GetMouse(&newPt);
  186.                 
  187.                 /* If the mouse hasn't moved, do nothing */
  188.                 if(newPt.h == lastPoint.h && newPt.v == lastPoint.v)
  189.                     continue;
  190.                 
  191.                 /* Set lastPoint for future comparison */
  192.                 lastPoint = newPt;
  193.                 
  194.                 /* Randomize point a little */
  195.                 newPt.h += Random() % 8;
  196.                 newPt.v += Random() % 8;
  197.                 
  198.                 /* If the point is outside the world now, do nothing */
  199.                 if(!PtInRect(newPt, &doc->world->portRect))
  200.                     continue;
  201.                 
  202.                 /* Stroke it. Need to convert to GWorld coords first*/
  203.                 newPt.h += GetCtlValue(doc->hScroll);
  204.                 newPt.v += GetCtlValue(doc->vScroll);
  205.                 StrokeBoth(newPt);
  206.             }
  207.         }
  208.     }
  209. }
  210.  
  211. /* Called when there is a click in the grow region. Just grows the window, with the 
  212. maximum size set to the offscreen size. */
  213. void AppGrowWindow(WindowPtr wind, Point where, Rect *desk)
  214. {
  215.     Rect            limits;
  216.     DocumentPeek    doc;
  217.     GWorldPtr        world;
  218.     long            size;
  219.     
  220.     /* Do nothing if not our window */
  221.     if(IsAppWindow(wind) == false)
  222.         return;
  223.         
  224.     doc = (DocumentPeek)wind;
  225.     world = doc->world;
  226.     
  227.     /* Set up size limits */
  228.     limits.top = limits.left = kMinWindowSize; /* One inch minimum */
  229.     /* These are actually bigger than the maximum size of the portRect, 
  230.     since the outline dragged by GrowWindow is the outline of the window frame,
  231.     not its portRect */
  232.     limits.right = world->portRect.right + kScrollAdjust + gDocFrameWidth;
  233.     limits.bottom = world->portRect.bottom + kScrollAdjust + gDocFrameWidth;
  234.     
  235.     /* Let the user grow the window */
  236.     size = GrowWindow(wind, where, &limits);
  237.     
  238.     /* If the size changed, then erase the scrollbars and grow box area and size the 
  239.     window, adjusting the scroll bars and so on */
  240.     if(size != 0)
  241.     {
  242.         Rect    tempRect;
  243.         short    hSize, vSize;
  244.         
  245.         SetPort(wind);
  246.         
  247.         /* Should erase the scrollbars and grow box area before resizing the window: */
  248.         
  249.         /* make a rect for the growBox */
  250.         tempRect = wind->portRect;
  251.         tempRect.top = tempRect.bottom - kScrollAdjust;
  252.         tempRect.left = tempRect.right - kScrollAdjust;
  253.         
  254.         /* Erase it and the scroll bars */
  255.         EraseRect(&tempRect);
  256.         HideControl(doc->hScroll);
  257.         HideControl(doc->vScroll);
  258.         
  259.         /* OK, size it, but first make sure that the size is within limits (users
  260.         can hold down the command key to bypass the limit, but we won't let 'em). 
  261.         Note that we subtract 1 from the limits to account for the window frame */
  262.         hSize = LoWord(size);
  263.         vSize = HiWord(size);
  264.         if(hSize > limits.right - 1) hSize = limits.right - gDocFrameWidth;
  265.         if(vSize > limits.bottom - 1) vSize = limits.bottom - gDocFrameWidth;
  266.         SizeWindow(wind, hSize, vSize, true);
  267.         ClipRect(&wind->portRect);
  268.         
  269.         /* adjust scrollBars */
  270.         AdjustScrollbars(wind, true);
  271.         
  272.         /* re-show controls if in the front */
  273.         if(FrontWindow() == wind)
  274.         {
  275.             ShowControl(doc->hScroll);
  276.             ShowControl(doc->vScroll);
  277.         }
  278.             
  279.         /* Make sure a redraw happens */
  280.         InvalRect(&wind->portRect);
  281.     }
  282. }
  283.  
  284. /* Called when the user clicks in the zoom box of a window. It calls a routine to find
  285. and install the appropriate zoom rect, then zooms it, adjusting the scroll bars and so 
  286. on */
  287. void     AppZoomWindow(WindowPtr wind, short zoomDir)
  288. {
  289.     Rect            worldRect;
  290.     DocumentPeek    doc;
  291.     
  292.     /* Do nothing if not our window */
  293.     if(IsAppWindow(wind) == false)
  294.         return;
  295.         
  296.     doc = (DocumentPeek)wind;
  297.     /* Get the maximum size of the window, and set up the window for the zoom */
  298.     worldRect = doc->world->portRect; /* top left is always 0, 0 */
  299.     ReadyWZoom(wind, zoomDir, worldRect.right + kScrollAdjust, worldRect.bottom + kScrollAdjust);
  300.     
  301.     /* Ok, zoom that sucker. Erase the window completely first: cosmetically this looks
  302.         good */
  303.     SetPort(wind);
  304.     EraseRect(&wind->portRect);
  305.     ZoomWindow(wind, zoomDir, true);
  306.     ClipRect(&wind->portRect);
  307.     InvalRect(&wind->portRect);
  308.  
  309.     /* Reset scroll bars, etc, hiding them first to avoid unnecessary drawing. We are
  310.     assuming here that the window is in front (it has to be to be zoomed */
  311.     HideControl(doc->hScroll);
  312.     HideControl(doc->vScroll);
  313.     AdjustScrollbars(wind, true);
  314.     ShowControl(doc->hScroll);
  315.     ShowControl(doc->vScroll);
  316. }
  317.     
  318. /* Called when there is a click in the menu bar, before the menu is shown. This is
  319. the app's opportunity to enable and disable menu items. */
  320. void    AppAdjustMenus()
  321. {
  322.     MenuHandle    mhndl;
  323.     long        pictSize, ignored;
  324.     Boolean        windowsUp;
  325.     
  326.     /* OK, first the file menu. If the windows are up, then enable Close, Save (if
  327.         the window needs saving), and Save As. Also enable Page Setup and Print if
  328.         the print record exists. If there are no windows open, disable same. Open 
  329.         and Quit are always enabled.  */
  330.     
  331.     windowsUp = ((WindowPeek)gSrcWindPtr)->visible;
  332.     mhndl = gShellMenuHandles[kFileMenu];
  333.     if(windowsUp)
  334.     {
  335.         EnableItem(mhndl, iClose);
  336.         if(((DocumentPeek)FrontWindow())->dirty)
  337.             EnableItem(mhndl, iSave);
  338.         else
  339.             DisableItem(mhndl, iSave);
  340.         EnableItem(mhndl, iSaveAs);
  341.         if(gPrintRecHandle != nil)
  342.         {
  343.             EnableItem(mhndl, iPageSetup);
  344.             EnableItem(mhndl, iPrint);
  345.         }
  346.     }
  347.     else        /* The windows are closed */
  348.     {
  349.         DisableItem(mhndl, iClose);
  350.         DisableItem(mhndl, iSave);
  351.         DisableItem(mhndl, iSaveAs);
  352.         DisableItem(mhndl, iPageSetup);
  353.         DisableItem(mhndl, iPrint);
  354.     }
  355.     
  356.     /* Now the Edit Menu. If the windows are up, enable Cut, Copy, and Clear 
  357.         automatically, and Undo and Paste conditionally. Otherwise, disable everything */
  358.         
  359.     mhndl = gShellMenuHandles[kEditMenu];
  360.     if(windowsUp)
  361.     {
  362.         EnableItem(mhndl, iCut);
  363.         EnableItem(mhndl, iCopy);
  364.         EnableItem(mhndl, iClear);
  365.         
  366.         /* For Paste: have to see if there is a PICT in the scrap */
  367.         pictSize = GetScrap(nil, 'PICT', &ignored);
  368.         if(pictSize > 0) /* We have a PICT in the scrap */
  369.             EnableItem(mhndl, iPaste);
  370.         else
  371.             DisableItem(mhndl, iPaste);
  372.         
  373.         /* Now Undo: have to see if the undo buffer exists and a target exists */
  374.         if(gUndoBuffer != nil && gUndoTarget != nil) /* We have a buffer and a target */
  375.             EnableItem(mhndl, iUndo);
  376.         else
  377.             DisableItem(mhndl, iUndo);
  378.     }
  379.     else
  380.     {
  381.         DisableItem(mhndl, iUndo);
  382.         DisableItem(mhndl, iCut);
  383.         DisableItem(mhndl, iCopy);
  384.         DisableItem(mhndl, iPaste);
  385.         DisableItem(mhndl, iClear);
  386.     }
  387. }    
  388.  
  389. /* called when a menu other than Apple, File, or Edit is used. */
  390. void AppMenu(short id, short item)
  391. {
  392.     switch(id)
  393.     {
  394.         case kFilterMenuID:
  395.             DoFilterMenu(item);
  396.             break;
  397.                 
  398.         case kBrushMenuID:
  399.             DoBrushMenu(item);
  400.             break;
  401.                 
  402.         case kAutoPaintMenuID:
  403.             DoPaintMenu(item);
  404.             break;
  405.         
  406.         default:
  407.             break;
  408.     }
  409. }
  410.  
  411. /* Called when the user selects "Open" from the File menu. In this app, opening a 
  412.     PICT file always puts the PICT into the source window, and clears the destination */
  413. void    AppOpen(void)
  414. {
  415.     SFTypeList            typeList;
  416.     short                numTypes, err;
  417.     StandardFileReply    reply;
  418.     
  419.     /* Close any windows that might be open now: if the user aborts a save, do nothing */
  420.     if(AppClose() == false)
  421.         return;
  422.  
  423.     /* only show PICT files */
  424.     typeList[0] = 'PICT';
  425.     
  426.     /* Standard File Dialog, system 7 style */
  427.     StandardGetFile(nil, 1, typeList, &reply);
  428.     
  429.     /* If they clicked "open"... */
  430.     if(reply.sfGood == true)
  431.     {
  432.         /* A big gnarly routine that sets everything up and reads in the pict */
  433.         err = ReadPICTFileToNewWorlds(&reply.sfFile);
  434.         if(err == noErr)
  435.         {
  436.             /* Restart the brush, to make sure there are no out of date values 
  437.             in the storage */
  438.             err = SetCurrentBrush(gCurrentBrushNum);
  439.             if(err != noErr)
  440.                 DoErrorAlert(kGenericErrorStr, err);
  441.         }
  442.         else
  443.             DoErrorAlert(kBadReadStr, err);
  444.     }
  445. }
  446.  
  447.  
  448. /* Called when the user selects "Close" from the File menu, chooses "Open" with a 
  449.     doc already open, or clicks the close box of a window. */            
  450. Boolean    AppClose(void)
  451. {
  452.     Boolean        rslt = false;
  453.     
  454.     /* Since the windows are in pairs, need to hide both of them. Note that we don't
  455.         throw them away, just hide them */
  456.     if(SaveCurrentDocs())
  457.     {
  458.         HideWindow(gSrcWindPtr);
  459.         HideWindow(gDstWindPtr);
  460.         
  461.         /* Turn off painting if it's on */
  462.         if(gPaintingNow == true)
  463.             DoPaintMenu(iStartPainting);
  464.         
  465.         /* Throw away the GWorlds to free up memory. */
  466.         KillGlobalGWorlds();
  467.          
  468.         /* Disable the appropriate menus */
  469.         DisableItem(gPaintMenuHandles[kFilterMenu], 0);
  470.         DisableItem(gPaintMenuHandles[kBrushMenu], 0);
  471.         DisableItem(gPaintMenuHandles[kAutoPaintMenu], 0);
  472.         DrawMenuBar();
  473.         
  474.         rslt = true;
  475.     }
  476.     return rslt;
  477. }
  478.  
  479. /* Called when the user selects "Save" from the File menu. Returns a boolean that is 
  480.     true only if AppSaveAs() is called and the user cancels the save, or if there is an
  481.     error. */
  482. Boolean    AppSave(void)
  483. {
  484.     DocumentPeek    doc;
  485.     OSErr            err = noErr;
  486.     Boolean            canceled = false;
  487.     WindowPtr        wind;
  488.     
  489.     /* Do nothing if the front window isn't ours */
  490.     wind = FrontWindow();
  491.     if(IsAppWindow(wind) == false)
  492.         return false;
  493.     
  494.     /* Get the window in question */
  495.     doc = (DocumentPeek)wind;
  496.  
  497.     /* Check the file name in our window's spec. If empty, do save as, else save */
  498.     if(*doc->fileSpec.name == 0)
  499.     {
  500.         canceled = AppSaveAs();
  501.     }
  502.     else    /* a regular save, it's been saved before */
  503.     {
  504.         /* Write the pict to the file */
  505.         err = WorldToExistingFile(&doc->fileSpec, doc->world);
  506.         /* It's possible that the user switched to the finder and deleted the
  507.             file or ejected the disk it's on, so let's check the error. If it's 
  508.             fnfErr or nsvErr, do a SaveAs */
  509.         if(err == fnfErr || err == nsvErr)
  510.         {
  511.             err = noErr;
  512.             canceled = AppSaveAs();
  513.         }
  514.         
  515.         if(err == noErr)
  516.             /* Successful save, so set dirty flag to false */
  517.             doc->dirty = false;
  518.         else
  519.         {
  520.             canceled = true;
  521.             if(err == memFullErr)
  522.                 DoErrorAlert(kNoMemStr, 0);
  523.             else
  524.                 DoErrorAlert(kBadWriteStr, err);
  525.         }
  526.     }
  527.     return canceled;
  528. }
  529.  
  530. /* Called when the user selects "Save As..." from the File menu. Returns a boolean that 
  531.     is true only if the user cancels the save. */
  532. Boolean    AppSaveAs(void)
  533. {
  534.     DocumentPeek        doc;
  535.     Str255                title, prompt;
  536.     short                promptID;
  537.     StandardFileReply    reply;
  538.     OSErr                err = noErr;
  539.     Boolean                canceled = false;
  540.     
  541.     if(IsAppWindow(FrontWindow()) == false)
  542.         return canceled;
  543.     
  544.     /* Get the window we are saving and its title, and select the appropriate prompt */
  545.     doc = (DocumentPeek)FrontWindow();
  546.     GetWTitle((WindowPtr)doc, title);
  547.     if(doc == &gSrcDoc)
  548.         promptID = kSrcSavePrompt;
  549.     else
  550.         promptID = kDstSavePrompt;
  551.     
  552.     /* put up Standard File Dialog */
  553.     StandardPutFile(TheStr(prompt, promptID), title,  &reply);
  554.     
  555.     /* If user clicked Save, do it */
  556.     if(reply.sfGood == true)
  557.     {
  558.         /* If we are replacing an existing file, we call one routine, if we are 
  559.             creating a new one, another routine */
  560.         if(reply.sfReplacing)
  561.             err = WorldToExistingFile(&reply.sfFile, doc->world);
  562.         else
  563.             err = WorldToNewFile(&reply.sfFile, doc->world);
  564.         
  565.         if(err == noErr)
  566.         {
  567.             /*    Successful save, so copy our file spec into the window info, 
  568.                 rename the window, and set the dirty flag */
  569.             doc->fileSpec = reply.sfFile;
  570.             SetWTitle((WindowPtr)doc, reply.sfFile.name);
  571.             doc->dirty = false;
  572.         }
  573.     }
  574.     else
  575.         canceled = true;
  576.  
  577.     if(err != noErr)
  578.     {
  579.         canceled = true;
  580.         if(err == memFullErr)
  581.             DoErrorAlert(kNoMemStr, 0);
  582.         else
  583.             DoErrorAlert(kBadWriteStr, err);
  584.     }
  585.     
  586.     return canceled;
  587. }
  588.  
  589. /* Called when the user selects "Page Setup..." from the File menu. */
  590. void AppPageSetup(void)
  591. {
  592.     short    err;
  593.     long    size, grow;
  594.     
  595.     if(gPrintRecHandle != nil)
  596.     {
  597.         PrOpen();                            /*  Open Print Mgr  */
  598.         err = PrError();                    /*  Check for errors  */
  599.         if(err == noErr)
  600.         {
  601.             PrStlDialog(gPrintRecHandle);    /* Do the style dialog */
  602.         }
  603.         PrClose();                            /*  Close Print Mgr  */
  604.     }
  605. }
  606.  
  607. /* Called when the user selects "Print..." from the File menu. */
  608. void AppPrint(void)
  609. {
  610.     GWorldPtr    world;
  611.     
  612.     /* Do nothing if the front window isn't ours, or if there is no print record */
  613.     if(IsAppWindow(FrontWindow()) == false || gPrintRecHandle == nil)
  614.         return;
  615.         
  616.     world = ((DocumentPeek)FrontWindow())->world;
  617.     Print(world);
  618. }
  619.  
  620. /* Called when the user selects "Undo" from the Edit menu. */
  621. void AppUndo(void)
  622. {
  623.     DocumentPeek    doc;
  624.     GWorldPtr        tempWorld;
  625.     
  626.     if(gUndoBuffer == nil || gUndoTarget == nil)
  627.         return;
  628.         
  629.     /* Get the target Doc */
  630.     doc = (DocumentPeek)gUndoTarget;
  631.     
  632.     /* Swap the worlds: gUndoBuffer gets attached to the doc, the doc's 
  633.         world becomes the new gUndoBuffer */
  634.     tempWorld = doc->world;
  635.     doc->world = gUndoBuffer;
  636.     gUndoBuffer = tempWorld;
  637.     
  638.     /* Update the Brush Params */
  639.     if(doc == &gSrcDoc)
  640.         gBrushStuff.theSource = doc->world;
  641.     else
  642.         gBrushStuff.theDestination = doc->world;
  643.  
  644.     /* Update the window */
  645.     SetPort(gUndoTarget);
  646.     InvalRect(&gUndoTarget->portRect);
  647. }
  648.  
  649. /* Called when the user selects "Cut" from the Edit menu. */
  650. void AppCut(void)
  651. {
  652.     DocumentPeek    doc;
  653.     WindowPtr        wind;
  654.     
  655.     /* Do nothing if the front window isn't ours */
  656.     wind = FrontWindow();
  657.     if(IsAppWindow(wind) == false)
  658.         return;
  659.         
  660.     /* First copy pict to clipboard */
  661.     if(AppCopy() == noErr)
  662.     {
  663.         /* Allow an undo of the cut */
  664.         SetUpForUndo(wind);
  665.     
  666.         /* Then clear the off world and request a window update */
  667.         doc = (DocumentPeek)wind;
  668.         if(doc->world != nil)
  669.         {
  670.             EraseOff(doc->world);
  671.         }
  672.         
  673.         doc->dirty = false;  /* assume no one wants to save an empty picture */
  674.         SetPort(wind);
  675.         InvalRect(&wind->portRect);
  676.     }
  677. }
  678.  
  679. /* Called when the user selects "Copy" from the Edit menu. */
  680. OSErr AppCopy(void)
  681. {
  682.     PicHandle        pict;
  683.     DocumentPeek    doc;
  684.     long            length;
  685.     OSErr            err = -1;
  686.     WindowPtr        wind;
  687.     
  688.     /* Do nothing if the front window isn't ours */
  689.     wind = FrontWindow();
  690.     if(IsAppWindow(wind) == false)
  691.         return noErr;
  692.     
  693.     /* Get the window we are copying from, and its off world */
  694.     doc = (DocumentPeek)wind;
  695.  
  696.     if(doc->world != nil)
  697.     {
  698.         err = ZeroScrap();
  699.         if(err == noErr)
  700.         {
  701.             err = -1; /* reset error */
  702.             /* Convert GWorld to a pict */
  703.             pict = WorldToPict(doc->world);
  704.             if(pict != nil)
  705.             {
  706.                 /* Figure out how big the pict is */
  707.                 length = GetHandleSize(pict);
  708.                 HLock(pict);
  709.                 
  710.                 /* Put it in the clipboard */
  711.                 err = PutScrap(length, 'PICT', *pict);
  712.                 HUnlock(pict);
  713.                 KillPicture(pict);
  714.             }
  715.             else
  716.                 err = memFullErr;
  717.         }
  718.     }
  719.     if(err != noErr)
  720.     {
  721.         if(err == memFullErr)
  722.             DoErrorAlert(kNoMemStr, 0);
  723.         else
  724.             DoErrorAlert(kGenericErrorStr, err);
  725.     }
  726.     return err;
  727. }            
  728.             
  729. /* Called when the user selects "Paste" from the Edit menu. */
  730. void AppPaste(void)
  731. {
  732.     PicHandle        pict;
  733.     Rect            frame;
  734.     DocumentPeek    doc;
  735.     long            length, dum;
  736.     WindowPtr        wind;
  737.     CGrafPtr        oldport;
  738.     GDHandle        olddev;
  739.     
  740.     /* Do nothing if the front window isn't ours */
  741.     wind = FrontWindow();
  742.     if(IsAppWindow(wind) == false)
  743.         return;
  744.     
  745.     /* Save the current port and device */    
  746.     GetGWorld(&oldport, &olddev);
  747.     
  748.     /* Make a small handle to hold the pict */
  749.     pict = (PicHandle)NewHandle(8L);
  750.     if(pict != nil)
  751.     {
  752.         /* Get the pict from the clipboard */
  753.         length = GetScrap(pict, 'PICT', &dum);
  754.         if(length > 0) /* got something */
  755.         {
  756.             /* Get the doc we are pasting into */
  757.             doc = (DocumentPeek)wind;
  758.             
  759.             /* Allow an undo */
  760.             SetUpForUndo(wind);
  761.             
  762.             /* Draw the picture */
  763.             SetGWorld(doc->world, nil);
  764.             frame = (**pict).picFrame;
  765.             DrawPicture(pict, &frame);
  766.  
  767.             /* Set the dirty flag for the window we pasted into to true, and 
  768.             Inval the port rect so it is updated */
  769.             doc->dirty = true;
  770.             SetGWorld(wind, olddev);
  771.             InvalRect(&wind->portRect);
  772.         }
  773.         else
  774.             DoErrorAlert(kGenericErrorStr, 0); /* No pict in clip, or couldn't get it */
  775.         DisposHandle((Handle)pict);
  776.     }
  777.     else
  778.         DoErrorAlert(kNoMemStr, 0);
  779. }
  780.  
  781. /* Called when the user selects "Clear" from the Edit menu. */
  782. void AppClear(void)
  783. {
  784.     DocumentPeek    doc;
  785.     WindowPtr        wind;
  786.     
  787.     /* Do nothing if the front window isn't ours */
  788.     wind = FrontWindow();
  789.     if(IsAppWindow(wind) == false)
  790.         return;
  791.         
  792.     doc = (DocumentPeek)wind;
  793.     
  794.     /* Allow an undo */
  795.     SetUpForUndo(wind);
  796.  
  797.     /* clear the off world and request a window update */
  798.     if(doc->world != nil)
  799.         EraseOff(doc->world);
  800.     doc->dirty = false;  /* assume no one wants to save an empty picture */
  801.     SetPort(wind);
  802.     InvalRect(&wind->portRect);
  803. }
  804.  
  805. /*     Called when the user chooses "Quit" from the File menu. If the user cancels
  806. the save it returns false, otherwise it returns true and the shell quits */
  807. Boolean AppQuit(void)
  808. {
  809.     /* returns false if the user cancels the save at any point, or if there is an error
  810.     saving */
  811.     return SaveCurrentDocs();
  812. }
  813.  
  814. /* Called when the shell is about to quit, Just deallocates memory. */
  815. void AppCleanUp(void)
  816. {
  817.     /* Unload the current brush */
  818.     if(gCurrentBrushHandle != nil)
  819.     {
  820.         CallBrush(kStopBrush, &gBrushStuff, gCurrentBrushHandle);
  821.         ReleaseResource(gCurrentBrushHandle);
  822.         gBrushStuff.storage = (long)nil;
  823.     }
  824.     
  825.     /* Kill the GWorlds */
  826.     KillGlobalGWorlds();
  827.     
  828.     /* Kill the print record, if there is one */
  829.     if(gPrintRecHandle != nil)
  830.         DisposHandle(gPrintRecHandle);
  831.         
  832.     /* Close the windows, which also kills the scrollBars */
  833.     if(gSrcWindPtr != nil)
  834.         CloseWindow(gSrcWindPtr);
  835.     if(gDstWindPtr != nil)
  836.         CloseWindow(gDstWindPtr);
  837. }
  838.  
  839.  
  840.